package jalgebrava.group.groups;

import static jalgebrava.util.collections.IterableUtil.*;
import jalgebrava.group.Group;
class BlackBoxGroupFactory {

	@SuppressWarnings("unused")
	private final static BlackBoxGroupFactory instance = new BlackBoxGroupFactory();
	private BlackBoxGroupFactory() {
		// hidden
	}
	final static <G> Group<Integer> mk(final String desc, int[][] inTable, final String[] elementNames) {
		
		final int n = inTable.length;
		for (int i=0;i<n;i++) {
			
			if (inTable[i].length != n) {
				throw new RuntimeException("Not a square table");
			}
			for (int j=0;j<n;j++) {
				if (inTable[i][j] < 0 || inTable[i][j] >= n) {
					throw new RuntimeException("Element out of range: " + inTable[i][j]);
				}
			}
		}
		
		// find a unit
		int unitCandidate = -1;
		for (int i=0;i<n;i++) {
			
			if (inTable[i][i] == i) {
				if (unitCandidate == -1) {
					unitCandidate = i;
				} else {
					throw new RuntimeException("More than one unit candidate");
				}
			}
		}
		if (unitCandidate==-1) {
			throw new RuntimeException("No unit");
		}
		final int unit = unitCandidate;
		
		final int inv[] = new int[n]; 
		for (int i=0;i<n;i++) {
			
			// find an inverse for i
			for (int j=0;j<n;j++) {
				if (inTable[i][j] == unit) {
					inv[i] = j;
					break;
				}
			}
		}
		final int[][] table = new int[n][n];
		for (int i=0;i<n;i++) {
			for (int j=0;j<n;j++) {
				table[i][j] = inTable[i][j];				
			}
		}
		
		return new AbstractGroup<Integer>(desc,integerRange(0, n),n) {

			@Override
			public boolean contains(Integer x) {
				return 0<=x && x < n;
			}

			@Override
			public Integer inv(Integer x) {
				return inv[x];
			}

			@Override
			public Integer mul(Integer x, Integer y) {
				return table[x][y];
			}

			@Override
			public Integer unit() {
				return unit;
			}

			@Override
			public String toShortString(Integer x) {
				if (elementNames == null) {
					return x.toString();
				}
				return elementNames[x];
			}

			
			
		};
	}
}
